动态代理

代理模式

为实际要访问的对象创建一个代理,客户不再直接访问原始对象,而是通过代理对象,间接访问原始对象,这样就可以控制客户对原始对象对访问,或者在访问原始对象前增加预处理等。代理模式常用的场景包括:

  1. 功能增强,在不修改原始对象接口的前提下增加新的功能。例如原先有一个FileOperator类,负责各种文件操作(打开,关闭,删除等等), 如果现在需要对于每个操作加上日志,怎么实现呢? 最简单的方法当然时直接修改FileOperator类,对每个操作加上日志,但是这种的缺点是:
    (1) 干扰FileOperator核心功能,FileOperator类添加了大量非核心代码,导致代码混乱,如果后续由大量额外功能,FileOperator会非常混乱,并且没法隔离不同的额外需求,例如有两个功能:(1)将log纪录到磁盘,(2)将log上传到服务器, 如果这两个功能只能2选1,那就非常麻烦了
    (2) 如果FileObserver来自于第三方库,可能无法修改
    (3) FileObserver来自于第三方,且可修改源码,这种情况如果将来需要合并新的代码,可能会出现大量到冲突
    因此通常都不会直接修改FileOperator类,而是通过代理的方式实现,创建一个代理类FileOperatorProxy(静态代理),并封装FileOprator类。这种方式非常像装饰者模式,主要区别在于,装饰者模式会定义统一的接口,装饰者必须实现被装饰者的所有接口,而静态代理只需要定义需要的接口,其他不需要访问的接口可以省略。

  2. 远程代理
    主要用于客户无法直接访问原始对象的情况,需要通过Proxy实现与远程对象的交互,例如Binder机制,客户端直接访问BinderProxy的接口,proxy通过Binder驱动与远端Binder对象进行交互。

静态代理

通过手动创建一个Proxy类,并且封装目标类,例如上述FileOperatorProxy。所有对FileOperator的访问都需要通过FileOpratorProxy预处理。静态代理最大的缺点在于对原始类对访问能力局限于Proxy类,如果想暴露原始类的所有接口,Proxy类就得定义原始类的所有接口的访问入口,如果接口很多,并且很多接口实际上是不需要预处理,则Proxy 类会写很多无用代码, 例如

  public class FileOperator {
        public void writeToFile() {
              //write content to file
        }
        public void getOperateCount() {
              //get operate count
        }
  }

实现一个Proxy 类,纪录log
public class FileOperatorProxy {
      private FileOperator target;
      public FileOperatorProxy(FileOperator target) {
              this.target = target;
      }
      public void writeToFile() {
            //print log
            target.writeToFile();
      }
      public int getOperateCount() {
            return target.getOperateCount();
      }
}

显示,FileOperatorProxy类的getOperateCount这个方法就很累赘,但是为了暴露FileOperator的getOperateCount方法,Proxy 类必须实现大量这样的无用接口,非常繁琐。这就有必要用到动态代理了。

动态代理

于静态代理的区别在于,静态代理类必须预先创建,而动态代理类是运行时根据原始类的接口(注意,必须是interface, 否则会抛异常)动态创建Proxy, 通过InvocationHandler接口,所有对Proxy对象对访问都统一调用InvocationHandler的invoke接口,这样,所有的增强操作都可以在invoke中完成,不需要增强的方法,直接调用原始对象的方法,因此动态代理的关键就在于实现InvocationHandler的invoke对象,对特定方法进行处理,其他方法不做任何处理:

public interface IFileOperator {
      void writeToFile();
      int getOperateCount();
}
public class FileOperator implements IFileOperator {
      @Override
      public void writeToFile() {
            //writeToFile
      }
      @Override
      public int getOperateCount() {
           //return operate count
      }
}
public class MyInvocationHandler implements InvocationHandler {
    FileOperator operator;
    public MyInvocationHandler(FileOperator operator) {
            this.operator = operator;
    }
   @Override
       public void invoke(Object proxy, Method method, Object[] args) {
            if (method.getName().equals("writeToFile")) {
                    //print log
                    operator.writeToFile();
            } else {
                  method.invoke(operator, args);
            }
       }
     
    public IFileOperator createProxy(IFileOperator operator) {
        IFileOperator proxy = (IFileOperator)       
        Proxy.newInstance(ClassLoader.getSystemClassLoader(), new 
        Class[] {IFileOperator.class}, new MyInvocationHandler(operator);
        return proxy;
}

可以看到,只需要对特定的方法做出来就行了,处于方法不做任何处理,就可以暴露原始对象的所有方法,避免了大量无用的重复方法, 这就是动态代理的优势。这里可能会有一个担心:每次创建代理的时候都需要动态创建一个Proxy Class,会不会有很多开销?实际上是不用担心的,Proxy.newInstance方法首先根据传入的interface列表创建Class,创建前会先检查对应的列表是否已经创建过Proxyclass,如果已创建,则复用,这样实际上每次调用Proxy.newInstance也只是创建了一个Proxy对象而已,开销跟静态代理是一样的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容